#ifndef _L_TRI_MESH_111225_H
#define _L_TRI_MESH_111225_H

#include <vector>

#include <lmacros.h>
#include <lvectors.h>
#include <ltriangle.h>

LLIB_BEGIN
namespace geom {

class TriMesh;

enum EdgeVertexName {
    EdgeVertexBegin = 0,
    EdgeVertexEnd   = 1
};

enum FaceVertexName {
    FaceVertexA = 0,
    FaceVertexB = 1,
    FaceVertexC = 2
};

class ModelEdge {
private:
    /* Constructor */
    ModelEdge(Vec3f& vertexBegin, Int32 vertexIndexBegin,
              Vec3f& vertexEnd, Int32 vertexIndexEnd, Int32 edgeIndex) :
        _vertexBegin        ( &vertexBegin ),
        _vertexIndexBegin   ( vertexIndexBegin ),
        _vertexEnd          ( &vertexEnd ),
        _vertexIndexEnd     ( vertexIndexEnd ),
        _index              ( edgeIndex ) {}

    /* edge index */
    Int32   _index;

    Vec3f*  _vertexBegin;
    Int32   _vertexIndexBegin;
    Vec3f*  _vertexEnd;
    Int32   _vertexIndexEnd;

public:
    inline Int32 GetIndex() const { return _index; }

    Vec3f& GetVertex (const EdgeVertexName& name) const;
    Vec3f& GetVertex (const Int32 name) const;

    const Int32 GetVertexIndex(const EdgeVertexName& name) const;
    const Int32 GetVertexIndex(const Int32 name) const;

    /* friends */
    friend class TriMesh;
};

class ModelFace {
private:
    /* Constructor */
    ModelFace(Vec3f& vertexA, Int32 vertexIndexA,
              Vec3f& vertexB, Int32 vertexIndexB,
              Vec3f& vertexC, Int32 vertexIndexC,
              Int32 faceIndex) :
        _vertexA(&vertexA), _vertexIndexA(vertexIndexA),
        _vertexB(&vertexB), _vertexIndexB(vertexIndexB),
        _vertexC(&vertexC), _vertexIndexC(vertexIndexC),
        _index(faceIndex) {}

    /* face index */
    Int32   _index;

    Vec3f*  _vertexA;
    Int32   _vertexIndexA;
    Vec3f*  _vertexB;
    Int32   _vertexIndexB;
    Vec3f*  _vertexC;
    Int32   _vertexIndexC;

public:
    inline Int32 GetIndex() const { return _index; }

    Vec3f& GetVertex(const FaceVertexName& name) const;
    Vec3f& GetVertex(const Int32& name) const;

    const Int32 GetVertexIndex(const FaceVertexName& name) const;
    const Int32 GetVertexIndex(const Int32& name) const;

    inline Tri3f GetTriangle() const { return Tri3f(*_vertexA, *_vertexB, *_vertexC); }

    inline const Boolean IsNeighbour(const ModelFace& another) const { return CheckNeighbour(*this,another); }

    inline static const Boolean CheckNeighbour(const ModelFace& first, const ModelFace& second) {
        return first._vertexA == second._vertexA || first._vertexA == second._vertexB || first._vertexA == second._vertexC ||
               first._vertexB == second._vertexA || first._vertexB == second._vertexB || first._vertexB == second._vertexC ||
               first._vertexC == second._vertexA || first._vertexC == second._vertexB || first._vertexC == second._vertexC;
    }

    /* friends */
    friend class TriMesh;
};

/* edge->face enum */
enum FaceType {
    FaceLeft = 0,
    FaceRight = 1
};
/* face->edge enum */
enum EdgeType {
    EdgeAB = 0,
    EdgeBC = 1,
    EdgeCA = 2
};

class TriMesh {
private:
    /* vertex list */
    Vec3f*  _vertexList;                /* input */
    Int32   _vertexNum;
    /* face vertex/edge index list */
    Vec3i*  _faceVertexIndexList;       /* input */
    Vec3i*  _faceEdgeIndexList;
    Int32   _faceNum;
    /* edge vertex/face index list */
    Vec2i*  _edgeVertexIndexList;
    Vec2i*  _edgeFaceIndexList;
    Int32   _edgeNum;
    /* model build */
    Boolean _modelBuild;

    const static Int32 InvalidFaceIndex = -1;

    /* private functions */
    void resetModelData(std::vector<Vec3f>& vertexList, std::vector<Vec3i>& faceVertexIndexList);
    void modelCopy(const TriMesh& m);

public:
    /* Constructor : with no data set */
    TriMesh();
    /* Constructor : input vertex list and face vertex index list */
    TriMesh(std::vector<Vec3f>& vertexList, std::vector<Vec3i>& faceVertexIndexList);
    /* Constructor : copy construction */
    TriMesh(const TriMesh& m);
    /* Destructor : delete all lists */
    ~TriMesh();

    /* assignment operator */
    TriMesh& operator=(const TriMesh& m);

    /* set model deta */
    void            SetModelData(std::vector<Vec3f>& vertexList, std::vector<Vec3i>& faceVertexIndexList);

    /* vertex query */
    Vec3f&          GetVertex(Int32 index);
    inline const Int32 GetVertexNum() const { return _vertexNum; }

    /* edge query */
    ModelEdge       GetEdge(Int32 index) const;
    ModelEdge       GetEdge(ModelFace& face, EdgeType edgeType) const;
    inline const Int32 GetEdgeNum() const { return _edgeNum; }

    /* face query */
    ModelFace       GetFace(Int32 index) const;
    ModelFace       GetFace(ModelEdge& edge, FaceType faceType) const;
    inline const Int32 GetFaceNum() const { return _faceNum; }
    Boolean         IsValidFace(ModelEdge& edge, FaceType faceType) const;

    /* internal vertex & index array query */
    inline const Float* GetVertexList() const { return (const Float*)_vertexList; }
    inline const Int32* GetFaceVertexIndexList() const { return (const Int32*)_faceVertexIndexList; }
};

} /* namespace geom */
LLIB_END

#endif
